﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Parsing;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.CSharp;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Threading;
using System.Reflection;
using System.Xml;

namespace Sample
{
    class Program
    {
        static unsafe void Main(string[] args)
        {
            Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

            Grammar grammarLanguageGrammar;
            const string GrammarLanguageGrammarFilePath = @"D:\Projects\Public\System.Text.Parsing\Sources\System.Text.Parsing\Grammar\GrammarLanguage.xml";
            using (var stream = File.OpenRead(GrammarLanguageGrammarFilePath))
            using (var xmlReader = XmlReader.Create(stream))
                grammarLanguageGrammar = Grammar.FromXml(xmlReader, GrammarLanguageGrammarFilePath, new TextLocation(0, 1, 1));
            var tokenizerDefinition = grammarLanguageGrammar.Tokenizers[0];

            // Generate.
            var tokenizerGenerator = new RegexTextTokenizerGrammarProcessor();
            tokenizerGenerator.Message += delegate(GrammarProcessorBase sender, bool isError, GrammarSourceRange sourceInfo, string text)
            {
                Console.WriteLine("{0}({1}): {2}: {3}", sourceInfo.SourceIdentity, sourceInfo.Range.Start.LineNumber, isError ? "error" : "warning", text);
            };
            using (var writer = new StreamWriter(@"D:\GrammarTokenizer.cs"))
                tokenizerGenerator.GenerateDesignTimeSourceCode("C#", "System.Text.Parsing", writer, tokenizerDefinition);
            using (var writer = new StreamWriter(@"D:\GrammarTokenizer.g.cs"))
                tokenizerGenerator.GenerateCompileTimeSourceCode("C#", "System.Text.Parsing", writer, tokenizerDefinition);

            var compiler = new CSharpCodeProvider();
            var compilerResults = compiler.CompileAssemblyFromFile(new CompilerParameters(new []
                {
                    new Uri(typeof(Regex).Assembly.GetName().CodeBase).LocalPath,
                    new Uri(typeof(ITokenizerContext).Assembly.GetName().CodeBase).LocalPath,
                })
                {
                    GenerateInMemory = true,
                    GenerateExecutable = false,
                    IncludeDebugInformation = true,
                }, @"D:\GrammarTokenizer.cs", @"D:\GrammarTokenizer.g.cs");


            var tokenizerType = compilerResults.CompiledAssembly.GetType("System.Text.Parsing.GrammarLanguageTokenizer");
            dynamic tokenizer = Activator.CreateInstance(tokenizerType);

            // Parse.
            var sourceData = File.ReadAllText(@"D:\Projects\Public\System.Text.Parsing\Sources\System.Text.Parsing\Grammar\Grammar.grammar");
            tokenizer.SetSourceData(sourceData, 0, sourceData.Length);
            tokenizer.SetStartLocation(new TextLocation(0, 1, 1));

            var scanMethod = typeof(Program).GetMethod("Scan", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(tokenizerType.GetNestedType("TokenValue"));

            var ctx = new MyContext();
            for (; ; )
            {
                var token = (TokenInfo)scanMethod.Invoke(null, new object[] { tokenizer, ctx });
                var tokenType = (int)token.TypeId;
                var tokenDef = tokenType == TokenTypeIds.Error || tokenType == TokenTypeIds.EOF ? null : tokenizerDefinition.Tokens[tokenType - TokenTypeIds.User];
                var tokenName = tokenType == TokenTypeIds.Error ? "ERROR" : (tokenType == TokenTypeIds.EOF ? "EOF" : tokenDef.Name.Text);
                if (tokenDef != null && tokenDef.Group.Name.Text == "ValueTokens")
                {
                    Console.WriteLine("{0,-32} {1}", tokenName, token.Value.RefType.Value0);
                }
                else
                {
                    Console.WriteLine("{0,-32}", tokenName);
                }
                if (token.TypeId == TokenTypeIds.EOF) break;
            }
            Console.ReadLine();
        }

        private struct TokenInfo
        {
            public int TypeId;
            public dynamic Value;
            public TextRange Range;
            public TokenOptions Options;
        }

        private static TokenInfo Scan<TTokenValue>(object tokenizer, MyContext ctx)
        {
            TokenInfo token;
            TTokenValue value = default(TTokenValue);
            ((ITokenizer<TextRange, TTokenValue>)tokenizer).Scan(ctx, out token.TypeId, out token.Options, out token.Range, ref value);
            token.Value = value;
            return token;
        }

        private sealed class MyContext : ITokenizerContext
        {
            public bool IsExpected(int tokenTypeId)
            {
                return true;
            }
        }
    }
}
